#include <math.h>

#include <iostream>

#include <omp.h>

#include "ogsolverBBPv3.h"

/**
 * A BBP solver : starts with jobs with last due dates
 *
 * Dans cette version 3 de la BBP, on demarre avec les jobs reordonnes dans le sens des dates dues croissantes. Mais
 * contrairement a l'exploration brutale de la version 2, cette fois on ne considere pas n'importe quel numero de batch.
 * Pour chaque job, on choisit de le rajouter soit dans un batch deja existant dont la capacite residuel est suffisante
 * pour stocker le nouveau job, soit dans un nouveau batch.
 *
 * Le nouveau batch est ordonnance avant les autres batchs deja crees.
 *
 * ATTENTION : Pour que cette solution soit optimale, sachant que l'on
 * part d'un probleme pour lequel les jobs sont classes par date due croissante, il est egalement necessaire que le cout de stockage
 * de tous les jobs soient identiques. Dans le cas contraire, la solution ainsi construite n'est plus certaine d'etre optimale.
 */
OGSolverBBPv3::OGSolverBBPv3(GSupplyLinkProblem& pb):OGSolverBBP(pb) {
}

OGSolverBBPv3::~OGSolverBBPv3() {
//    if (this->bestSolution != NULL ) delete this->bestSolution ;
//    if (this->bestSolutionDueDateOrdered != NULL ) delete this->bestSolutionDueDateOrdered ;
//    if (this->currentSolutionDueDateOrdered != NULL ) delete this->currentSolutionDueDateOrdered ;
}


/**
 *
 */
void OGSolverBBPv3::solveInit() {
    cout << "GSupplyLinkSameBatchSolverBBP::solveMain()... " << endl ;

//    this->bestSolution = NULL ;
//    this->bestSolutionDueDateOrdered = NULL ;
//    this->currentSolutionDueDateOrdered = new OGSupplyLinkSolutionPartial(this->problem) ;

    initBestSolutionWithFFD() ;

    double totalVolume = 0 ;
    for (int i=0;i<this->problem.getNP();++i)
        totalVolume+=this->problem.getJobSize(i) ;
    minBatch = int(totalVolume/this->problem.getTransporter(0).getCapacity()) ;
    if (minBatch*this->problem.getTransporter(0).getCapacity()<totalVolume) minBatch++ ;
    maxBatch = this->problem.getNP() ;

    this->currentSolutionDueDateOrdered.setNbrBatch(0) ;

}

/**
 *
 */
void OGSolverBBPv3::solveMain() {
//    vector<double> tabBatchRemainingCapacity(maxBatch) ;
//    for (int b=0;b<maxBatch;b++)
//        tabBatchRemainingCapacity[b] = this->problem.getTransporter(0).getCapacity() ;
    vector<double> tabBatchRemainingCapacity ;

    // Single process
    cout << "depthFirstSearchSingle() started" << endl ;
    depthFirstSearchSingle(0, this->problem.getNP()-1, this->currentSolutionDueDateOrdered, tabBatchRemainingCapacity) ;

    // Transform "bestSolutionDueDateOrdered" to "bestSolution"
    bestSolution = this->problem.getBestSolutionFromBestSolutionDueDateOrdered(bestSolutionDueDateOrdered) ;

//    if (this->currentSolutionDueDateOrdered) delete this->currentSolutionDueDateOrdered ;
//    this->currentSolutionDueDateOrdered = NULL ;

    cout << "End of BBPv3" << endl ;
}

/**
 *
 */
void OGSolverBBPv3::solveEnd() {
    // Transform "bestSolutionDueDateOrdered" to "bestSolution"
    bestSolution = this->problem.getBestSolutionFromBestSolutionDueDateOrdered(bestSolutionDueDateOrdered) ;

//    if (this->currentSolutionDueDateOrdered) delete this->currentSolutionDueDateOrdered ;
//    this->currentSolutionDueDateOrdered = NULL ;

    cout << "End of BBP" << endl ;
}

/**
 * DepthFirstSearch (BBP) with a single process
 */
void OGSolverBBPv3::depthFirstSearchSingle(int nbrbatch, int job, OGSupplyLinkSolutionPartial& sol, vector<double>& tabBatchRemainingCapacity) {

    // Evaluation of the newly built solution
    if (job<0) {
        OGSupplyLinkSolutionPartial newsol (sol) ;
        //newsol.setStartEnd(0,this->problem.getNP()-1) ;
        //newsol.compacteBatches() ;
        double eval = newsol.evaluate() ;
        if (bestSolutionDueDateOrdered.getEvaluation()<0 || eval<bestSolutionDueDateOrdered.getEvaluation()) {
            bestSolutionDueDateOrdered = newsol ;
        }
        return ;
    }

    // Add job in a batch, if batchjob==-1 => create a new batch before
    for (int batchPotentialIndice=nbrbatch-1;batchPotentialIndice>=-1;batchPotentialIndice--) {

        double jobsize = this->problem.getJobSize(job) ;
        int batchRealIndice=-1 ;
        int incnbrbatch = -1 ;


        // Job is added to an existing batch
        if (batchPotentialIndice>=0 && tabBatchRemainingCapacity[batchPotentialIndice]>=jobsize) {
            batchRealIndice= batchPotentialIndice ;
            incnbrbatch = 0 ;
        }

        // Creation of a new batch
        if (batchPotentialIndice<0) {
            batchRealIndice=0 ;
            incnbrbatch = 1 ;
        }

        // Transformation of the solution
        if (incnbrbatch>=0) {
            vector<double> tabBatchRemainingCapacity2(tabBatchRemainingCapacity) ;
            OGSupplyLinkSolutionPartial sol2 (sol) ;

            if (incnbrbatch>0) {
//                for (int ind2=0;ind2<tabBatchRemainingCapacity.size()-1;ind2++)
//                    tabBatchRemainingCapacity[ind2]++ ;
                tabBatchRemainingCapacity2.insert(tabBatchRemainingCapacity2.begin(),this->problem.getTransporter(0).getCapacity()) ;
                // The batch indices of previously placed jobs should be increased
                for (int job2=job+1;job2<problem.getNP();job2++) {
                    sol2.setBatchIndice(job2,1+sol2.getBatchIndice(job2)) ;
                }
            }
            tabBatchRemainingCapacity2[batchRealIndice] -= jobsize ;
            sol2.setBatchIndice(job, batchRealIndice+1) ;
            sol2.setDeliveryCompletionTime(job, (double)batchRealIndice+1) ;
            sol2.setNbrBatch(nbrbatch+incnbrbatch) ;

            double lowerBound = sol2.evaluatePartial(job, this->problem.getNP()-1);
            assert (lowerBound>=0) ;

            // Cut
            bool cut = false ;
            double bestSolDueDateOrderedEval = bestSolutionDueDateOrdered.getEvaluation() ;
            if (bestSolDueDateOrderedEval>=0 && lowerBound>=bestSolDueDateOrderedEval) {
                cut = true ;
            }
            if (!cut)
                depthFirstSearchSingle(nbrbatch+incnbrbatch, job-1, sol2, tabBatchRemainingCapacity2) ;

        }
    }
}
